/*
===========================================================================
This source file is part of DyLab (Dynamic Laboratory)
For the latest info, see http://dylab.googlecode.com

Copyright (c) 2006-2008 Lukas Krejci
(krejci.lukas@volny.cz)

This file is part of DyLab.

    DyLab is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    DyLab is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with DyLab.  If not, see <http://www.gnu.org/licenses/>.
===========================================================================
*/

#ifndef __dylab_EdgeComplex_h__
#define __dylab_EdgeComplex_h__

//===========================================================================================
#include <dylab/utils/HashGenerator.h>
#include <dylab/utils/topology/NodeComplex.h>

//===========================================================================================
namespace dylab {
            
    /** EdgeComplex is 1-simplical _complex.
     */   
    template <
        class _NodeSimplexDataT = EmptyClass,
        typename _EdgeSimplexDataT = EmptyClass
    >
    class EdgeComplex 
        : public NodeComplex<_NodeSimplexDataT>
    {           
    public:
        // only for convenience (and typing ;-)
        typedef typename NodeComplex<_NodeSimplexDataT>  NodeComplexT;
        typedef typename EdgeComplex<_NodeSimplexDataT, _EdgeSimplexDataT>  EdgeComplexT;
    public:
        //-----------------------------------------------------------------------------
        /// EdgeSimplex is 1-simplex.
        class EdgeSimplex 
            : public Simplex
            , public _EdgeSimplexDataT
        {      
        public:
            static const order_t ORDER = ORDER_1;
        private:
            ///
            nodeSimplexID_t mNodeSimplexIds[EdgeGeometry3::V_COUNT];
        public:
            /**
             */
            EdgeSimplex(EdgeComplexT * _complex, nodeSimplexID_t _n1, nodeSimplexID_t _n2)
                : Simplex(ORDER, _complex->generateEdgeID(_n1, _n2), _complex, true, false)
                    { 
                        // add all edge nodes, if not added yet
                        _attachNode(_n1);
                        _attachNode(_n2);

                        // store node ids
                        mNodeSimplexIds[EdgeGeometry3::V_0] = _n1;
                        mNodeSimplexIds[EdgeGeometry3::V_1] = _n2;

                        // notify that the edge creation is done
                        getEdgeComplex()->_notifySimplexCreated(this); 
                    }
            /**
             */
            virtual ~EdgeSimplex()
                    { }
            /** 
             */
            EdgeComplexT * getEdgeComplex()
                    { return dynamicCast<EdgeComplexT>(getComplex()); }
            /** 
             */
            const EdgeComplexT * getEdgeComplex() const
                    { return dynamicCast<EdgeComplexT>(getComplex()); }
            /** 
             */
            nodeSimplexID_t getNodeID(EdgeGeometry3::vertexidx_t _vertexIdx) const
                    { return mNodeSimplexIds[_vertexIdx]; }
            /** 
             */
            bool hasNode(nodeSimplexID_t _nodeID) const
                    { 
                        bool found = false;
                        for (EdgeGeometry3::VertexIdxIterator vertexIdx; vertexIdx.isValid(); vertexIdx++)
                        {
                            if (getNodeID(vertexIdx) == _nodeID)
                            {
                                found = true;
                                break;
                            }
                        }

                        return found;
                    }
        private:
            /**
             */
            void _attachNode(nodeSimplexID_t _n)
                    {
                        // estrablish connection with the node
                        connect(getEdgeComplex()->getNode(_n));
                    }
        };
        //-----------------------------------------------------------------------------

    public:
        /// builds a vector of all edge nodes
        class EdgeNodes 
            : public SimplexNodes
        {
        public:
            EdgeNodes(EdgeSimplex * _edge)
                : SimplexNodes(_edge)
                    { }
        };
        //-----------------------------------------------------------------------------

    public:
        // only for convenience (and typing ;-)
		typedef typename EdgeSimplex::id_t edgeSimplexID_t;
        /// declare node container
        typedef HashObjContainer<edgeSimplexID_t, EdgeSimplex> EdgeSimplexMap;

    public:
        /// builds a map of all simplex edges
        /// (i.e., 1-simplices, that are direct or undirect sub-simplices of the specified _simplex)
        class SimplexEdges
        {
        private:
            Simplex * mSourceSimplex;
            EdgeSimplexMap mEdges;
        public:
            SimplexEdges(Simplex * _simplex)
                : mSourceSimplex(_simplex), mEdges(false)
                    { 
                        DYLAB_ASSERT(mSourceSimplex != NULL);
                        //TODO
                    }
        };
        //-----------------------------------------------------------------------------

    public:
        /// merges a map of all adjacent nodes of the specified _node or _edge
        class AdjacentNodes : public AdjacentSimplices
        {
        protected:
            AdjacentNodes()
                : AdjacentSimplices(NodeSimplex::ORDER)
                    { }
        public:
            AdjacentNodes(NodeSimplex * _node)
                : AdjacentSimplices(NodeSimplex::ORDER)
                    { mergeSuperAdjacentSimplices(_node); }
            AdjacentNodes(EdgeSimplex * _edge)
                : AdjacentSimplices(NodeSimplex::ORDER)
                    { mergeSuperAdjacentSimplices(_edge->getSubSimplices()); }
        };
        //-----------------------------------------------------------------------------

    public:
        /// merges a map of all adjacent edges of the specified _node or _edge
        class AdjacentEdges : public AdjacentSimplices
        {
        protected:
            AdjacentEdges()
                : AdjacentSimplices(EdgeSimplex::ORDER)
                    { }
        public:
            AdjacentEdges(NodeSimplex * _node)
                : AdjacentSimplices(EdgeSimplex::ORDER)
                    { mergeAdjacentSuperSimplices(_node); }
            AdjacentEdges(EdgeSimplex * _edge)
                : AdjacentSimplices(EdgeSimplex::ORDER)
                    { mergeSubAdjacentSimplices(_edge); }
        };
        //-----------------------------------------------------------------------------
    public:

        /**
        */
        EdgeComplex()
                { }

        /**
         */           
        virtual ~EdgeComplex()
                { }

        /**
         */           
        EdgeSimplex * getEdge(edgeSimplexID_t _edgeID)
                {  return dynamicCast<EdgeSimplex>(simplicesReader(EdgeSimplex::ORDER).get(_edgeID)); }

        /**
         */           
        const EdgeSimplex * getEdge(edgeSimplexID_t _edgeID) const
                {  return dynamicCast<const EdgeSimplex>(simplicesReader(EdgeSimplex::ORDER).get(_edgeID)); }

        /**
         */           
        edgeSimplexID_t generateEdgeID(nodeSimplexID_t _n1, nodeSimplexID_t _n2) const
                { return HashGenerator<nodeSimplexID_t>::SortedAndFactorised<>().addValue(_n1, 157).addValue(_n2, 397).generate().getHash(); }
    };
    
}
//===========================================================================================
#endif // __dylab_EdgeComplex_h__
